package com.redribbon.droidnoise;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.util.Log;

/**
 * MediaPlayer does not yet support streaming from external URLs so this class provides a pseudo-streaming function
 * by downloading the content incrementally & playing as soon as we get enough audio in our temporary storage.
 */
public class MediaStreamer {

	//Probably don't need this...
	//private TextView textStreamed;	
	//private ImageButton playButton;	
	//private ProgressBar progressBar;
	//private int counter = 0;
	
	//Track for display by progressBar
	//private long mediaLengthInKb, mediaLengthInSeconds;
	private int totalKbRead = 0;
	
	// Create Handler to call View updates on the main UI thread.
	private final Handler handler = new Handler();

	private MediaPlayer mediaPlayer;
	
	private File downloadingMediaFile; 
	
	private boolean isInterrupted;
	
	private Context context;

    int totalBytesRead = 0, incrementalBytesRead = 0;
    
    FileOutputStream out;
	
	Runnable r;
	
 	public MediaStreamer(Context context) {
 		this.context = context;
 		
        downloadingMediaFile = new File(context.getCacheDir(),"downloadingMedia_.dat");
        
        try {
        	out = new FileOutputStream(downloadingMediaFile);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
		/*
		Runnable r = new Runnable() {  
			public void run() {
				 
	        	
	            try {   
	        		downloadAudioIncrement(mediaStream);
	            } catch (IOException e) {
	            	Log.e(getClass().getName(), "Unable to initialize the MediaPlayer", e);
	            	return;
	            }  
	            catch (InterruptedException e) {
	            	Log.e(getClass().getName(), "Ahhhh!!!!1!!shift-1!!");
	            }
	        }
	    }; 
	    new Thread(r).start();*/
	}
	
    /**  
     * Progressivly download the media to a temporary location and update the MediaPlayer as new content becomes available.
     */  
    public void startStreaming(final InputStream mediaStream) throws IOException {    	

		Log.i("taylor info", "starting stream read");
		
		Runnable r = new Runnable() {  
			public void run() {
				 
	        	
	            try {   
	        		downloadAudioIncrement(mediaStream);
	            } catch (IOException e) {
	            	Log.e(getClass().getName(), "Unable to initialize the MediaPlayer", e);
	            	return;
	            }  
	            catch (InterruptedException e) {
	            	Log.e(getClass().getName(), "Ahhhh!!!!1!!shift-1!!");
	            }
	        }
		};
	    handler.post(r);  
	    
    	//this.mediaLengthInKb = mediaLengthInKb;
    	//this.mediaLengthInSeconds = mediaLengthInSeconds;
    	/*
		Runnable r = new Runnable() {   
	        public void run() {   
	            try {   
	        		downloadAudioIncrement(mediaStream);
	            } catch (IOException e) {
	            	Log.e(getClass().getName(), "Unable to initialize the MediaPlayer", e);
	            	return;
	            }  
	            catch (InterruptedException e) {
	            	Log.e(getClass().getName(), "Ahhhh!!!!1!!shift-1!!");
	            }
	        }   
	    };   */
    }
    
    /**  
     * Download the url stream to a temporary location and then call the setDataSource  
     * for that local file
     * @throws IOException 
     * @throws InterruptedException 
     */  
    public void downloadAudioIncrement(InputStream mediaStream) throws IOException, InterruptedException {

    	InputStream stream = mediaStream;
    	  
        byte buf[] = new byte[65537];

        //do {
    		Log.i("taylor info", "trying to read");
        	
        	int numread = stream.read(buf);   
        	if (numread <= 0) {
        		return;
        	}
                
            out.write(buf, 0, numread);
            totalBytesRead += numread;
            incrementalBytesRead += numread;
            totalKbRead = totalBytesRead/1000;
            
            testMediaBuffer();
           	fireDataLoadUpdate();
        //} while (validateNotInterrupted());   

        if (validateNotInterrupted()) {
	       	fireDataFullyLoaded();
           	//fireDataLoadUpdate();
        }
       	stream.close();
    }  

    private boolean validateNotInterrupted() {
		if (isInterrupted) {
			if (mediaPlayer != null) {
				Log.e("StreamingMediaPlayer", "oh nos pausing!");
				mediaPlayer.pause();
				//mediaPlayer.release();
			}
			return false;
		} else {
			return true;
		}
    }

    
    /**
     * Test whether we need to transfer buffered data to the MediaPlayer.
     * Interacting with MediaPlayer on non-main UI thread can causes crashes to so perform this using a Handler.
     */  
    private void  testMediaBuffer() {
        //Log.e("StreamingMediaPlayer", "in testmediabuffer");
	    Runnable updater = new Runnable() {
	        public void run() {
	            if (mediaPlayer == null) {
	            	//  Only create the MediaPlayer once we have the minimum buffered data
	            	if ( totalKbRead >= NoiseConstants.INTIAL_KB_BUFFER) {
	            		try {
		            		startMediaPlayer();
	            		} catch (Exception e) {
	            			Log.e(getClass().getName(), "Error copying buffered conent.", e);    			
	            		}
	            	}
	            } else if ( mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() <= 1000 ){ 
	            	//  NOTE:  The media player has stopped at the end so transfer any existing buffered data
	            	//  We test for < 1second of data because the media player can stop when there is still
	            	//  a few milliseconds of data left to play
	            	transferBufferToMediaPlayer();
	            } else {
//	                Log.e("StreamingMediaPlayer", "duration: " + mediaPlayer.getDuration() + 
//	                		" current pos: " + mediaPlayer.getCurrentPosition());
	            }
	        }
	    };
	    handler.post(updater);
    }
    
    private void startMediaPlayer() {
        try {           	
	    	//File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
        	File bufferedFile = new File(context.getCacheDir(),"playingMedia.dat");
        	moveFile(downloadingMediaFile,bufferedFile);
    		
        	Log.e("Player",bufferedFile.length()+"");
        	Log.e("Player",bufferedFile.getAbsolutePath());
        	
    		mediaPlayer = new MediaPlayer();
    		FileInputStream fis = new FileInputStream(bufferedFile); 
    		FileDescriptor fd = fis.getFD(); 
    		mediaPlayer.setDataSource(fd);
        	mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    		mediaPlayer.prepare();
        	fireDataPreloadComplete();
        	
        } catch (IOException e) {
        	Log.e(getClass().getName(), "Error initializing the MediaPlaer.", e);
        	return;
        }   
    }
    
    /**
     * Transfer buffered data to the MediaPlayer.
     * Interacting with MediaPlayer on non-main UI thread can causes crashes to so perform this using a Handler.
     */  
    private void transferBufferToMediaPlayer() {
	    try {

            Log.e("StreamingMediaPlayer", "before duration: " + mediaPlayer.getDuration() + 
            		" current pos: " + mediaPlayer.getCurrentPosition());
	    	// First determine if we need to restart the player after transferring data...e.g. perhaps the user pressed pause
	    	boolean wasPlaying = mediaPlayer.isPlaying();
	    	int curPosition = mediaPlayer.getCurrentPosition();
	    	mediaPlayer.reset();

	    	//File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
	    	File bufferedFile = new File(context.getCacheDir(),"playingMedia.dat");
	    	moveFile(downloadingMediaFile,bufferedFile);
        	
			//mediaPlayer = new MediaPlayer();
    		FileInputStream fis = new FileInputStream(bufferedFile);
    		//BufferedInputStream bis = new BufferedInputStream(fis);
    		FileDescriptor fd = fis.getFD(); 
    		mediaPlayer.setDataSource(fd);
    		fis.close();
    		//mediaPlayer.setDataSource(bufferedFile.getAbsolutePath());
    		//mediaPlayer.setAudioStreamType(AudioSystem.STREAM_MUSIC);
    		mediaPlayer.prepare();
    		mediaPlayer.seekTo(curPosition);
    		

            Log.e("StreamingMediaPlayer", "after duration: " + mediaPlayer.getDuration() + 
            		" current pos: " + mediaPlayer.getCurrentPosition());
    		
    		//  Restart if at end of prior beuffered content or mediaPlayer was previously playing.  
    		//	NOTE:  We test for < 1second of data because the media player can stop when there is still
        	//  a few milliseconds of data left to play
    		boolean atEndOfFile = mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() <= 1000;
        	if (wasPlaying || atEndOfFile){
        		Log.e("StreamingMediaPlayer", "Restarting media player");
        		mediaPlayer.start();
        	}
        	else
        	{
        		Log.e("StreamingMediaPlayer", "Not restarting media player");
        	}
        	
		}catch (Exception e) {
	    	Log.e(getClass().getName(), "Error updating to newly loaded content.", e);            		
		}
    }
    
    private void fireDataLoadUpdate() {
		Runnable updater = new Runnable() {
	        public void run() {
	        	//textStreamed.setText((CharSequence) (totalKbRead + " Kb read"));
	    		//float loadProgress = ((float)totalKbRead/(float)mediaLengthInKb);
	    		//progressBar.setSecondaryProgress((int)(loadProgress*100));
	        }
	    };
	    handler.post(updater);
    }
    
    /**
     * We have preloaded enough content and started the MediaPlayer so update the buttons & progress meters.
     */
    private void fireDataPreloadComplete() {
    	Runnable updater = new Runnable() {
	        public void run() {
	    		mediaPlayer.start();
	    		startPlayProgressUpdater();
	        	//playButton.setEnabled(true);
	        	//streamButton.setEnabled(false);
	        }
	    };
	    handler.post(updater);
    }

    private void fireDataFullyLoaded() {
		Runnable updater = new Runnable() { 
			public void run() {
   	        	transferBufferToMediaPlayer();
	        	//textStreamed.setText((CharSequence) ("Audio full loaded: " + totalKbRead + " Kb read"));
	        }
	    };
	    handler.post(updater);
    }
    
    public MediaPlayer getMediaPlayer() {
    	return mediaPlayer;
	}
	
    public void startPlayProgressUpdater() {
    	//float progress = (((float)mediaPlayer.getCurrentPosition()/1000)/(float)mediaLengthInSeconds);
    	//progressBar.setProgress((int)(progress*100));
    	
		if (mediaPlayer.isPlaying()) {
			Runnable notification = new Runnable() {
		        public void run() {
		        	startPlayProgressUpdater();
				}
		    };
		    handler.postDelayed(notification,1000);
    	}
		else
		{
			Log.e("StreamingMediaPlayer", "Why is the mediaplayer not playing?");
		}
    }    
    
    public void interrupt() {
    	//playButton.setEnabled(false);
    	isInterrupted = true;
    	validateNotInterrupted();
    }
    
	public void moveFile(File oldLocation, File	newLocation) throws IOException {
		if ( oldLocation.exists( )) {
			BufferedInputStream  reader = new BufferedInputStream( new FileInputStream(oldLocation) );
			BufferedOutputStream  writer = new BufferedOutputStream( new FileOutputStream(newLocation, false));
            try {
		        byte[]  buff = new byte[8192];
		        int numChars;
		        while ( (numChars = reader.read(  buff, 0, buff.length ) ) != -1) {
		        	writer.write( buff, 0, numChars );
      		    }
            } catch( IOException ex ) {
				throw new IOException("IOException when transferring " + oldLocation.getPath() + " to " + newLocation.getPath());
            } finally {
                try {
                    if ( reader != null ){
                    	writer.close();
                        reader.close();
                    }
                } catch( IOException ex ){
				    Log.e(getClass().getName(),"Error closing files when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() ); 
				}
            }
        } else {
			throw new IOException("Old location does not exist when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() );
        }
	}
	
}
